/*
 * @Description: from
 */
const form = {}

// 指令名以大写字母开头，中间全小写，否则jest会报错
form.install = function (app, option = {}) {
  // 清除修改标记
  app.config.globalProperties.resetChange = function (ref) {
    const target = this.$refs[ref].$el
    if (!target) return
    const changeList = target.querySelectorAll('.el-input.change')
    Array.from(changeList).forEach((item) => {
      item.classList.remove('change')
      item.querySelector('input').removeAttribute('foremost')
    })
  }

  // 文本输入
  // <el-input v-format.text />
  // 整数输入
  // <el-input v-format.integer />
  // 允许负数的整数输入
  // <el-input v-format.integer.negative />
  // 浮点数输入 (默认保留2位小数)
  // <el-input v-format.float />
  // 自定义小数位数的浮点数输入
  // <el-input v-format.float="{ decimal: 3 }" />
  // 允许负数的浮点数输入
  // <el-input v-format.float.negative />
  // 不需要千分位分隔符
  // <el-input v-format.float="{ grouping: false }" />
  // 字段类型约束
  app.directive('format', {
    mounted(el, binding) {
      const evtHandlers = {}
      // 修改获取 input 元素的方式
      let targetEl = el.tagName === 'INPUT' ? el : el.querySelector('input')
      if (!targetEl) return

      // 获取组件实例的新方式
      const vm =
        binding.instance ||
        (el.__vueParentComponent ? el.__vueParentComponent.proxy : null)

      // 确保获取到组件实例并添加默认方法
      if (!vm) {
        console.warn('无法获取组件实例')
        return
      }

      // 添加默认方法
      if (!vm.handleInput) {
        vm.handleInput = (evt) => {
          const value = evt.target.value
          vm.$emit('input', value)
          vm.$emit('update:modelValue', value)
        }
      }

      // 确保其他必要方法存在
      if (!vm.handleBlur) {
        vm.handleBlur = (evt) => {
          vm.$emit('blur', evt)
        }
      }

      const {
        modifiers: {
          text = false,
          integer = false,
          float = false,
          negative = false,
        } = {},
        value: {
          grouping = true,
          decimal = 2,
          blur = true,
          change = false,
          length = -1, // 整数长度，只是显示长度，并不影响输入长度
        } = {},
      } = binding

      if (text) {
        if (!targetEl) return

        const toggleChange = (value) => {
          const foremost = targetEl.getAttribute('foremost')
          const parentEl = targetEl.parentNode
          if (foremost === null) {
            if (value === '') {
              parentEl.classList.remove('change')
            } else {
              parentEl.classList.add('change')
            }
          } else if (foremost !== value) {
            parentEl.classList.add('change')
          } else {
            parentEl.classList.remove('change')
          }
        }

        const focusHandler = (evt) => {
          const value = targetEl.value
          if (targetEl.getAttribute('foremost') === null) {
            targetEl.setAttribute('foremost', value)
          }
          change && toggleChange(value)
        }
        targetEl.addEventListener('focus', focusHandler, false)
        evtHandlers.focus = focusHandler

        const blurHandler = (evt) => {
          const value = targetEl.value
          change && toggleChange(value)
        }
        targetEl.addEventListener('blur', blurHandler, false)
        evtHandlers.blur = blurHandler

        el.evtHandlers = evtHandlers
      } else if (integer || (float && decimal <= 0)) {
        if (!targetEl) return

        // 非正常小数设定作为整数处理
        const revise = (value) => {
          if (!value) return ''

          let ret = value.replace(/\D/g, '')
          if (negative && value.startsWith('-')) {
            ret = `-${ret}`
          }
          return ret
        }

        const toggleChange = (value) => {
          const foremost = targetEl.getAttribute('foremost')
          const parentEl = targetEl.parentNode
          if (foremost === null) {
            if (value === '') {
              parentEl.classList.remove('change')
            } else {
              parentEl.classList.add('change')
            }
          } else if (foremost.replace(/,/g, '') !== value.replace(/,/g, '')) {
            parentEl.classList.add('change')
          } else {
            parentEl.classList.remove('change')
          }
        }

        const focusHandler = (evt) => {
          try {
            const rules = vm.elFormItem.getRules()
            const [{ limit }] = rules.filter((item) => item.limit)
            const [minVal, maxVal] = limit.slice(1, -1).split(/,/)
            const maxLength = Math.max(minVal.length, maxVal.length) + 1 // 进一位
            targetEl.setAttribute('maxlength', maxLength)
          } catch (e) {
            console.warn('请配置rule规则，否则无法限定输入')
          }
          let value = revise(targetEl.value)
          // 保存修正后的值
          if (targetEl.getAttribute('foremost') === null) {
            targetEl.setAttribute('foremost', value)
          }
          change && toggleChange(value)
          // 对整数 0 特殊修正
          if (/^-?0+$/.test(value)) value = ''
          targetEl.value = value
          vm.handleInput(evt)
        }
        targetEl.addEventListener('focus', focusHandler, false)
        evtHandlers.focus = focusHandler

        const pasteHandler = (evt) => {
          vm.handleInput(evt)
        }
        targetEl.addEventListener('paste', pasteHandler, false)
        evtHandlers.paste = pasteHandler

        const keypressHandler = (evt) => {
          let prevent = false
          const selection = document.getSelection().toString()
          const isEmpty = selection === ''
          try {
            const rules = vm.elFormItem.getRules()
            const [{ limit }] = rules.filter((item) => item.limit)
            const minEqual = limit[0] === '['
            const maxEqual = limit[limit.length - 1] === ']'
            let [minVal, maxVal] = limit.slice(1, -1).split(/,/)
            const elValue = targetEl.value
            let value = parseInt(elValue)
            value = !isNaN(value) ? value : 0
            maxVal = parseInt(maxVal)
            minVal = parseInt(minVal)

            if (elValue && elValue !== '-' && !/\.$/.test(value) && isEmpty) {
              if (minVal > 0) {
                // 全正
                if (
                  (maxEqual ? value > maxVal : value >= maxVal) &&
                  evt.key !== '-'
                ) {
                  // 允许"正改负"
                  prevent = true
                }
                if (value < 0) {
                  prevent = true
                }
              } else if (minVal === 0) {
                if (
                  (maxEqual ? value > maxVal : value >= maxVal) &&
                  evt.key !== '-'
                ) {
                  // 允许"正改负"
                  prevent = true
                }
                if (value < minVal) {
                  prevent = true
                }
              } else {
                if (maxVal < 0) {
                  // 全负
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                  if (value > 0) {
                    prevent = true
                  }
                } else if (maxVal === 0) {
                  if (value > maxVal && evt.key !== '-') {
                    // 允许"正改负"
                    prevent = true
                  }
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                } else {
                  // 负正
                  if (
                    (maxEqual ? value > maxVal : value >= maxVal) &&
                    evt.key !== '-'
                  ) {
                    // 允许"正改负"
                    prevent = true
                  }
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                }
              }
            }
          } catch (e) {
            console.warn('请配置rule规则，否则无法限定输入')
          }
          if ((negative ? /[^\d-]/ : /\D/).test(evt.key)) {
            prevent = true
          }
          prevent && evt.preventDefault()
          vm.handleInput(evt)
        }
        targetEl.addEventListener('keypress', keypressHandler, false) // 'keydown' evt.keyCode 坑多
        evtHandlers.keypress = keypressHandler

        const compositionendHandler = (evt) => {
          targetEl.value = revise(targetEl.value)
          vm.handleInput(evt)
        }
        targetEl.addEventListener(
          'compositionend',
          compositionendHandler,
          false
        )
        evtHandlers.compositionend = compositionendHandler

        const inputHandler = (evt) => {
          if (evt.inputType === 'insertCompositionText') {
            // 解决中文输入法之后数据无法响应式问题
            if (navigator.platform.includes('Mac')) {
              vm.isComposing = false // 手动结束输入状态，原因见 ElementUI 源码中 handleInput 方法
            } else if (vm.isComposing) {
              return
            }
          }
          targetEl.value = revise(targetEl.value)
          vm.handleInput(evt)
        }
        targetEl.addEventListener('input', inputHandler, false)
        evtHandlers.input = inputHandler

        const blurHandler = (evt) => {
          let value = revise(targetEl.value)
          const foremost = targetEl.getAttribute('foremost')
          if (!value) {
            if (!/^-?0+$/.test(foremost)) {
              return change && toggleChange(value)
            } else {
              value = foremost // 恢复整数 0
            }
          }

          const intValue = parseInt(value)
          let fmtValue = !isNaN(intValue) ? (value !== '-0' ? intValue : 0) : '' // '-0' 修正为 0

          if (grouping) {
            fmtValue = fmtValue.toLocaleString()
          }
          if (length > 0) {
            if (fmtValue >= Math.pow(10, length)) {
              fmtValue = Number(fmtValue.toString().slice(0, length))
            }
          }
          targetEl.value = fmtValue
          if (evt) {
            vm.handleInput(evt)
            blur && vm.handleBlur(evt)
            change && toggleChange(fmtValue)
          } else {
            vm.$emit('input', fmtValue)
            if (fmtValue === '') return

            try {
              const rules = vm.elFormItem.getRules()
              const [{ trigger }] = rules.filter((item) => item.limit)
              trigger.forEach((item) => {
                // 手动触发校验
                vm.dispatch('ElFormItem', `el.form.${item}`, fmtValue)
              })
            } catch (e) {
              console.warn('请配置rule规则，否则无法限定输入')
            }
          }
        }
        targetEl.addEventListener('blur', blurHandler, false)
        evtHandlers.blur = blurHandler

        el.evtHandlers = evtHandlers

        vm.$nextTick(blurHandler)
      } else if (float) {
        if (!targetEl) return

        const revise = (value) => {
          if (!value) return ''

          let ret = value.replace(/[^0-9.]/g, '')
          if (negative && value.startsWith('-')) {
            ret = `-${ret}`
          }
          return ret
        }

        const toggleChange = (value) => {
          const foremost = targetEl.getAttribute('foremost')
          const parentEl = targetEl.parentNode
          if (foremost === null) {
            if (value === '') {
              parentEl.classList.remove('change')
            } else {
              parentEl.classList.add('change')
            }
          } else if (foremost.replace(/,/g, '') !== value.replace(/,/g, '')) {
            parentEl.classList.add('change')
          } else {
            parentEl.classList.remove('change')
          }
        }

        const focusHandler = (evt) => {
          try {
            const rules = vm.elFormItem.getRules()
            const [{ limit }] = rules.filter((item) => item.limit)
            const [minVal, maxVal] = limit.slice(1, -1).split(/,/)
            const maxLength =
              Math.max(
                parseInt(minVal).toString().length,
                parseInt(maxVal).toString().length
              ) +
              1 +
              decimal +
              1 // 小数点 + 小数位 + 进一位
            targetEl.setAttribute('maxlength', maxLength)
          } catch (e) {
            console.warn('请配置rule规则，否则无法限定输入')
          }
          let value = revise(targetEl.value)
          // 保存修正后的值
          if (targetEl.getAttribute('foremost') === null) {
            targetEl.setAttribute('foremost', value)
          }
          change && toggleChange(value)
          // 对小数 0 特殊修正
          if (/^-?(0+|0\.0+)$/.test(value)) value = ''
          targetEl.value = value
          vm.handleInput(evt)
        }
        targetEl.addEventListener('focus', focusHandler, false)
        evtHandlers.focus = focusHandler

        const pasteHandler = (evt) => {
          vm.handleInput(evt)
        }
        targetEl.addEventListener('paste', pasteHandler, false)
        evtHandlers.paste = pasteHandler

        const keypressHandler = (evt) => {
          let prevent = false
          const selection = document.getSelection().toString()
          const isEmpty = selection === ''
          try {
            const rules = vm.elFormItem.getRules()
            const [{ limit }] = rules.filter((item) => item.limit)
            const minEqual = limit[0] === '['
            const maxEqual = limit[limit.length - 1] === ']'
            let [minVal, maxVal] = limit.slice(1, -1).split(/,/)
            const elValue = targetEl.value
            let value = parseFloat(elValue)

            value = !isNaN(value) ? value : 0
            maxVal = parseFloat(maxVal)
            minVal = parseFloat(minVal)

            if (
              elValue &&
              elValue !== '-' &&
              !/\.$/.test(elValue) &&
              isEmpty &&
              evt.key !== '.'
            ) {
              // 输入非 '.'
              if (minVal > 0) {
                // 全正
                if (
                  (maxEqual ? value > maxVal : value >= maxVal) &&
                  evt.key !== '-'
                ) {
                  // 允许"正改负"
                  prevent = true
                }
                if (value < 0) {
                  prevent = true
                }
              } else if (minVal === 0) {
                if (
                  (maxEqual ? value > maxVal : value >= maxVal) &&
                  evt.key !== '-'
                ) {
                  // 允许"正改负"
                  prevent = true
                }
                if (value < minVal) {
                  prevent = true
                }
              } else {
                if (maxVal < 0) {
                  // 全负
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                  if (value > 0) {
                    prevent = true
                  }
                } else if (maxVal === 0) {
                  if (value > maxVal && evt.key !== '-') {
                    // 允许"正改负"
                    prevent = true
                  }
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                } else {
                  // 负正
                  if (
                    (maxEqual ? value > maxVal : value >= maxVal) &&
                    evt.key !== '-'
                  ) {
                    // 允许"正改负"
                    prevent = true
                  }
                  if (minEqual ? value < minVal : value <= minVal) {
                    prevent = true
                  }
                }
              }
            }
          } catch (e) {
            console.warn('请配置rule规则，否则无法限定输入')
          }
          if (
            (negative ? /[^0-9-.]/ : /[^0-9.]/).test(evt.key) ||
            (evt.key === '.' &&
              /\./.test(targetEl.value) &&
              !selection.includes('.'))
          ) {
            // 最多输入一个.
            prevent = true
          }
          prevent && evt.preventDefault()
          vm.handleInput(evt)
        }
        targetEl.addEventListener('keypress', keypressHandler, false) // 'keydown' evt.keyCode 坑多
        evtHandlers.keypress = keypressHandler

        const compositionendHandler = (evt) => {
          targetEl.value = revise(targetEl.value)
          vm.handleInput(evt)
        }
        targetEl.addEventListener(
          'compositionend',
          compositionendHandler,
          false
        )
        evtHandlers.compositionend = compositionendHandler

        const inputHandler = (evt) => {
          if (evt.inputType === 'insertCompositionText') {
            // 解决中文输入法之后数据无法响应式问题
            if (navigator.platform.includes('Mac')) {
              vm.isComposing = false // 手动结束输入状态，原因见 ElementUI 源码中 handleInput 方法
            } else if (vm.isComposing) {
              return
            }
          }
          targetEl.value = revise(targetEl.value)
          vm.handleInput(evt)
        }
        targetEl.addEventListener('input', inputHandler, false)
        evtHandlers.input = inputHandler

        const blurHandler = (evt) => {
          let value = revise(targetEl.value)
          const foremost = targetEl.getAttribute('foremost')
          if (!value) {
            if (!/^-?(0+|0\.0+)$/.test(foremost)) {
              return change && toggleChange(value)
            } else {
              value = foremost // 恢复小数 0
            }
          }

          const floatVaue = parseFloat(value)
          const multiple = Math.pow(10, decimal)
          let fmtValue = (
            Math.round((!isNaN(floatVaue) ? floatVaue : 0) * multiple) /
            multiple
          ).toString()
          if (!fmtValue.includes('.')) {
            fmtValue += '.' + '0'.repeat(decimal)
          } else {
            fmtValue += '0'.repeat(decimal - fmtValue.split('.')[1].length)
          }

          if (grouping) {
            const [intPart, fltPart] = fmtValue.split('.')
            fmtValue = `${parseInt(intPart).toLocaleString()}.${fltPart}`
          }
          if (length > 0) {
            if (fmtValue >= Math.pow(10, length)) {
              fmtValue = Number(fmtValue.toString().slice(0, length))
            }
          }
          targetEl.value = fmtValue
          if (evt) {
            vm.handleInput(evt)
            blur && vm.handleBlur(evt)
            change && toggleChange(fmtValue)
          } else {
            vm.$emit('input', fmtValue)
            if (fmtValue === '') return

            try {
              const rules = vm.elFormItem.getRules()
              const [{ trigger }] = rules.filter((item) => item.limit)
              trigger.forEach((item) => {
                // 手动触发校验
                vm.dispatch('ElFormItem', `el.form.${item}`, fmtValue)
              })
            } catch (e) {
              console.warn('请配置rule规则，否则无法限定输入')
            }
          }
        }
        targetEl.addEventListener('blur', blurHandler, false)
        evtHandlers.blur = blurHandler

        el.evtHandlers = evtHandlers

        vm.$nextTick(blurHandler)
      }
    },
    unmounted(el) {
      const targetEl = el.querySelector('input') // 此时 vm 不存在
      if (!targetEl) return

      const evtHandlers = el.evtHandlers || {}
      for (const evt in evtHandlers) {
        targetEl.removeEventListener(evt, evtHandlers[evt], false)
      }
      el.evtHandlers = null
    },
  })

  // 溢出隐藏显示title
  app.directive('title', {
    mounted: function (el, binding) {
      const padding = binding.value || 24
      el.addEventListener('mouseenter', (evt) => {
        const target = evt.target
        const { offsetWidth } = target // 目标元素宽
        let clientWidth = 10000
        if (evt.fromElement) {
          clientWidth = evt.fromElement.clientWidth - padding // 父元素宽
        }
        target.title = target.innerText
        if (offsetWidth < clientWidth) target.title = ''
      })
    },
  })

  // 防止按钮多次重复点击
  app.directive('throttle', {
    inserted(el, binding) {
      const interval = binding.value || 1200
      el.addEventListener('click', () => {
        if (!el.disabled) {
          el.disabled = true
          setTimeout(() => {
            el.disabled = false
          }, interval)
        }
      })
    },
  })

  const numberValue = (el, limit, float) => {
    const val = el.target.value
    let _val = ''
    if (/^[1-9]\d*$/.test(val)) {
      _val = val
    } else {
      // 支持小数
      if (float && /^(([1-9]{1}\d*)|(0{1}))(\.\d{0,})?$/.test(val)) {
        _val = val
      } else {
        const m = val.match(/\d/g) || ['']
        _val = m.reduce((a, b) => a + '' + b) || ''
      }
    }

    if (limit && Array.isArray(limit)) {
      const l = limit.length
      if (l == 2 && _val !== '') {
        // 范围值 最大值 最小值
        if (_val < limit[0] || _val > limit[1]) {
          _val = _val < limit[0] ? limit[0] : _val > limit[1] ? limit[1] : _val
        }
      }
    }

    el.target.value = _val
  }

  /* 只能输入数字包含小数.,利用parseInt */
  app.directive('Onlynumber', {
    mounted: function (el, binding) {
      const limit = binding.value
      el.addEventListener('keyup', (ev) => {
        const target = ev.target || ev.srcElement
        if (target.className.indexOf('el-input__inner') > -1) {
          numberValue(ev, limit)
          if (el.__vue__) {
            el.__vue__.handleInput(ev)
            el.__vue__.handleChange(ev.target.value)
          }
        }
      })
      el.addEventListener('input', (ev) => {
        const target = ev.target || ev.srcElement
        if (target.className.indexOf('el-input__inner') > -1) {
          numberValue(ev, limit)
          if (el.__vue__) {
            el.__vue__.handleInput(ev)
            el.__vue__.handleChange(ev.target.value)
          }
        }
      })
    },
  })

  /* 只能输入数字包含小数.,利用parseInt */
  app.directive('Numberfloat', {
    mounted: function (el, binding) {
      const limit = binding.value
      el.addEventListener('keyup', (ev) => {
        const target = ev.target || ev.srcElement
        if (target.className.indexOf('el-input__inner') > -1) {
          numberValue(ev, limit, true)
          if (el.__vue__) {
            el.__vue__.handleInput(ev)
            el.__vue__.handleChange(ev.target.value)
          }
        }
      })
      el.addEventListener('input', (ev) => {
        const target = ev.target || ev.srcElement
        if (target.className.indexOf('el-input__inner') > -1) {
          numberValue(ev, limit, true)
          if (el.__vue__) {
            el.__vue__.handleInput(ev)
            el.__vue__.handleChange(ev.target.value)
          }
        }
      })
    },
  })

  const doResize = (el, value, $table) => {
    // 如果设定了高度，就使用设定的值
    const bottomOffset = (value && value.bottomOffset) || 65
    const height =
      window.innerHeight - el.getBoundingClientRect().top - bottomOffset
    el.style.height = `${height}px`
    // $table.layout.setHeight(height)
    // $table.doLayout()
  }
  app.directive('adaptive', {
    async mounted(el, binding, vnode) {
      const { value } = binding
      const { componentInstance: $table } = await vnode
      if ($table.height !== 'auto' || (value && value.preventOption)) {
        return
      }
      el.resizeListener = async () => {
        await doResize(el, value, $table)
      }
      el.resizeListener()
      window.addEventListener('resize', el.resizeListener)
    },
    unbind(el) {
      window.removeEventListener('resize', el.resizeListener)
    },
  })
}

export default form
